﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;


namespace SimpleORM
{
    public class ExpressionTranslater : ExpressionVisitor
    {
        #region Properties - Public

        public IProviderDatabase DataSource { get; set; }

        public IScriptFactory Result { get; protected set; }
        public ObjectContext ResultObjectContext { get; protected set; }
        public ObjectContext ElementObjectContext { get; protected set; }
        public Expression Expression { get; protected set; }

        public eExpressionResultType ResultType { get; protected set; }

        #endregion

        #region Properties - Temp

        protected object[] ResultsPriority { get; set; }
        protected Stack<IScriptFactory> CurrentStack { get; set; }

        #endregion

        private Hashtable htHandler;

        private SortedList<string, Stack<IScriptFactory>> sts;

        public ExpressionTranslater()
        {
            htHandler = ExpressionTranslaterAttribute.CreateHandlers(this.GetType());

            sts = new SortedList<string, Stack<IScriptFactory>>();
        }

        #region Methods - Public

        public virtual Expression Translate(Expression expression)
        {
            Expression = Visit(expression);

            Result = BuildScript();

            return Expression;
        }

        #endregion

        #region Methods - Visit

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            Expression exp = Visit(m.Arguments[0]);

            string name = m.Method.Name;
            CurrentStack = GetStack(name);

            if (!htHandler.ContainsKey(name))
                throw new NotSupportedMethodException(name);

            try
            {
                MethodInfo methodInfo = htHandler[name] as MethodInfo;
                if (methodInfo != null)
                {
                    methodInfo.Invoke(this, new object[] { m });
                }
            }

            catch (Exception ex)
            {
                //string msg = string.Format(new EdProvider().
                //    GetName(EdProvider.langTransalteExpressionFail), m, name, ex);

                //Tracker.WriteLine(new TrackerObject()
                //{
                //    Description = msg,
                //    EventID = EdProvider.langTransalteExpressionFail,
                //    Grade = eMessageGrade.Error,
                //    Source = this.GetType().FullName,
                //});

                throw new TranslateMethodException(name, ex.InnerException);
            }

            return exp;

            //List<Expression> list = new List<Expression>();
            //list.Add(exp);
            //for (int i = 1; i < m.Arguments.Count; i++)
            //    list.Add(m.Arguments[i]);

            //return Expression.Call(null, m.Method, list);
        }

        #endregion

        #region Methods - Handler

        #endregion

        #region Methods - Build

        protected virtual IScriptFactory OnHandleResult(string name, IScriptFactory f)
        {
            return f;
        }

        protected IScriptFactory BuildScript()
        {
            BeginBuildScript();
            IScriptFactory f = BuildScript(ResultsPriority);
            EndBuildScript(f);

            return f;
        }

        protected virtual IScriptFactory BuildScript(object[] ps)
        {
            IScriptFactory rlt = DataSource.CreateScriptFactory().New();
            bool found = false;

            foreach (object item in ps)
            {
                if (item is object[])
                    rlt = rlt.Concat(BuildScript(item as object[]));

                else if (item is string && !found)
                {
                    string key = item.ToString();

                    Stack<IScriptFactory> st = GetStack(key);
                    if (st.Count == 0)
                        continue;

                    rlt = rlt.Concat(OnHandleResult(key, st.Pop()));
                    found = true;
                }
            }

            return rlt;
        }

        protected virtual void BeginBuildScript()
        {
        }

        protected virtual void EndBuildScript(IScriptFactory result)
        {
        }

        #endregion

        #region Methods - Stack

        protected Stack<IScriptFactory> GetStack(string name)
        {
            if (!sts.ContainsKey(name))
                sts[name] = new Stack<IScriptFactory>();

            return sts[name];
        }

        #endregion
    }
}